home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / bin / cpan2dist < prev    next >
Text File  |  2009-10-01  |  22KB  |  675 lines

  1. #!/usr/bin/perl
  2.     eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
  3.     if $running_under_some_shell;
  4. #!/usr/bin/perl -w
  5. use strict;
  6. use CPANPLUS::Backend;
  7. use CPANPLUS::Dist;
  8. use CPANPLUS::Internals::Constants;
  9. use Data::Dumper;
  10. use Getopt::Long;
  11. use File::Spec;
  12. use File::Temp                  qw|tempfile|;
  13. use File::Basename;
  14. use Locale::Maketext::Simple    Class => 'CPANPLUS', Style => 'gettext';
  15.  
  16. local $Data::Dumper::Indent = 1;
  17.  
  18. use constant PREREQ_SKIP_CLASS  => 'CPANPLUS::To::Dist::PREREQ_SKIP';
  19. use constant ALARM_CLASS        => 'CPANPLUS::To::Dist::ALARM';
  20.  
  21. ### print when you can
  22. $|++;
  23.  
  24. my $cb      = CPANPLUS::Backend->new
  25.                 or die loc("Could not create new CPANPLUS::Backend object");
  26. my $conf    = $cb->configure_object;
  27.  
  28. my %formats = map { $_ => $_ } CPANPLUS::Dist->dist_types;
  29.  
  30. my $opts    = {};
  31. GetOptions( $opts,
  32.             'format=s',             'archive',
  33.             'verbose!',             'force!',
  34.             'skiptest!',            'keepsource!',
  35.             'makefile!',            'buildprereq!',
  36.             'help',                 'flushcache',
  37.             'ban=s@',               'banlist=s@',
  38.             'ignore=s@',            'ignorelist=s@',
  39.             'defaults',             'modulelist=s@',
  40.             'logfile=s',            'timeout=s',
  41.             'dist-opts=s%',         'set-config=s%',
  42.             'default-banlist!',     'set-program=s%',
  43.             'default-ignorelist!',  'edit-metafile!',
  44.             'install!'
  45.         );
  46.         
  47. die usage() if exists $opts->{'help'};
  48.  
  49. ### parse options
  50. my $tarball     = $opts->{'archive'}    || 0;
  51. my $keep        = $opts->{'keepsource'} ? 1 : 0;
  52. my $prereqbuild = exists $opts->{'buildprereq'}
  53.                     ? $opts->{'buildprereq'}
  54.                     : 0;
  55. my $timeout     = exists $opts->{'timeout'} 
  56.                     ? $opts->{'timeout'} 
  57.                     : 300;
  58.  
  59. ### use default answers?
  60. $ENV{'PERL_MM_USE_DEFAULT'} = $opts->{'defaults'} ? 1 : 0;
  61.  
  62. my $format;
  63. ### if provided, we go with the command line option, fall back to conf setting
  64. {   $format      = $opts->{'format'}         || $conf->get_conf('dist_type');
  65.     $conf->set_conf( dist_type  => $format );
  66.  
  67.     ### is this a valid format??
  68.     die loc("Invalid format: " . ($format || "[NONE]") ) . usage() 
  69.         unless $formats{$format};
  70.  
  71.     ### any options to fix config entries
  72.     {   my $set_conf = $opts->{'set-config'} || {};
  73.         while( my($key,$val) = each %$set_conf ) {
  74.             $conf->set_conf( $key => $val );
  75.         }
  76.     }        
  77.  
  78.     ### any options to fix program entries
  79.     {   my $set_prog = $opts->{'set-program'} || {};
  80.         while( my($key,$val) = each %$set_prog ) {
  81.             $conf->set_program( $key => $val );
  82.         }
  83.     }        
  84.  
  85.     ### any other options passed
  86.     {   my %map = ( verbose     => 'verbose',
  87.                     force       => 'force',
  88.                     skiptest    => 'skiptest',
  89.                     makefile    => 'prefer_makefile'
  90.                 );
  91.                 
  92.         ### set config options from arguments        
  93.         while (my($key,$val) = each %map) {
  94.             my $bool = exists $opts->{$key} 
  95.                             ? $opts->{$key} 
  96.                             : $conf->get_conf($val);
  97.             $conf->set_conf( $val => $bool );
  98.         }    
  99.     }        
  100. }
  101.  
  102. my @modules = @ARGV;
  103. if( exists $opts->{'modulelist'} ) {
  104.     push @modules, map { parse_file( $_ ) } @{ $opts->{'modulelist'} }; 
  105.  
  106. die usage() unless @modules;
  107.  
  108. ### set up munge callback if requested
  109. {   if( $opts->{'edit-metafile'} ) {
  110.         my $editor = $conf->get_program('editor');
  111.         
  112.         if( $editor ) {
  113.     
  114.             ### register install callback ###
  115.             $cb->_register_callback(
  116.                 name    => 'munge_dist_metafile',
  117.                 code    => sub {
  118.                                 my $self = shift;
  119.                                 my $text = shift or return;
  120.                             
  121.                                 my($fh,$file) = tempfile( UNLINK => 1 );
  122.                                 
  123.                                 unless( print $fh $text ) {
  124.                                     warn "Could not print metafile information: $!";
  125.                                     return;
  126.                                 }
  127.                                 
  128.                                 close $fh;
  129.                                 
  130.                                 system( $editor => $file );
  131.                                 
  132.                                 my $cont = $cb->_get_file_contents( file => $file );
  133.                                 
  134.                                 return $cont;
  135.                             },
  136.             );
  137.             
  138.         } else {
  139.             warn "No editor configured. Can not edit metafiles!\n";
  140.         }
  141.     }
  142. }
  143.  
  144. my $fh;
  145. LOGFILE: {
  146.     if( my $file = $opts->{logfile} ) {
  147.         open $fh, ">$file" or ( 
  148.             warn loc("Could not open '%1' for writing: %2", $file,$!),
  149.             last LOGFILE
  150.         );            
  151.         
  152.         warn "Logging to '$file'\n";
  153.         
  154.         *STDERR = $fh;
  155.         *STDOUT = $fh;
  156.     }
  157. }
  158.  
  159. ### reload indices if so desired
  160. $cb->reload_indices() if $opts->{'flushcache'};
  161.  
  162. {   my @ban      = exists $opts->{'ban'}  
  163.                             ? map { qr/$_/ } @{ $opts->{'ban'} }
  164.                             : ();
  165.  
  166.  
  167.     if( exists $opts->{'banlist'} ) {
  168.         push @ban, map { parse_file( $_, 1 ) } @{ $opts->{'banlist'} };
  169.     }
  170.     
  171.     push @ban,  map  { s/\s+//; $_ }
  172.                 map  { [split /\s*#\s*/]->[0] }
  173.                 grep { /#/ }
  174.                 map  { split /\n/ } _default_ban_list() 
  175.         if $opts->{'default-banlist'};
  176.     
  177.     ### use our prereq install callback 
  178.     $conf->set_conf( prereqs => PREREQ_ASK );
  179.     
  180.     ### register install callback ###
  181.     $cb->_register_callback(
  182.             name    => 'install_prerequisite',
  183.             code    => \&__ask_about_install,
  184.     );
  185.  
  186.     
  187.     ### check for ban patterns when handling prereqs
  188.     sub __ask_about_install {
  189.   
  190.         my $mod     = shift or return;
  191.         my $prereq  = shift or return;
  192.     
  193.     
  194.         ### die with an error object, so we can verify that
  195.         ### the die came from this location, and that it's an
  196.         ### 'acceptable' death
  197.         my $pat = ban_me( $prereq );
  198.         die bless sub { loc("Module '%1' requires '%2' to be installed " .
  199.                         "but found in your ban list (%3) -- skipping",
  200.                         $mod->module, $prereq->module, $pat ) 
  201.                   }, PREREQ_SKIP_CLASS if $pat;
  202.         return 1;
  203.     }    
  204.     
  205.     ### should we skip this module?
  206.     sub ban_me {
  207.         my $mod = shift;
  208.         
  209.         for my $pat ( @ban ) {
  210.             return $pat if $mod->module =~ /$pat/i;
  211.         }
  212.         return;
  213.     }
  214. }    
  215.  
  216. ### patterns to strip from prereq lists
  217. {   my @ignore      = exists $opts->{'ignore'}  
  218.                         ? map { qr/$_/ } @{ $opts->{'ignore'} }
  219.                         : ();
  220.  
  221.     if( exists $opts->{'ignorelist'} ) {
  222.         push @ignore, map { parse_file( $_, 1 ) } @{ $opts->{'ignorelist'} };
  223.     }
  224.  
  225.     push @ignore, map  { s/\s+//; $_ }
  226.                   map  { [split /\s*#\s*/]->[0] }
  227.                   grep { /#/ }
  228.                   map  { split /\n/ } _default_ignore_list() 
  229.         if $opts->{'default-ignorelist'};
  230.  
  231.     
  232.     ### register install callback ###
  233.     $cb->_register_callback(
  234.             name    => 'filter_prereqs',
  235.             code    => \&__filter_prereqs,
  236.     );
  237.  
  238.     sub __filter_prereqs {
  239.         my $cb      = shift;
  240.         my $href    = shift;
  241.         
  242.         for my $name ( keys %$href ) {
  243.             my $obj = $cb->parse_module( module => $name ) or (
  244.                 warn "Cannot make a module object out of ".
  245.                         "'$name' -- skipping\n",
  246.                 next );
  247.  
  248.             if( my $pat = ignore_me( $obj ) ) {
  249.                 warn loc("'%1' found in your ignore list (%2) ".
  250.                          "-- filtering it out\n", $name, $pat);
  251.  
  252.                 delete $href->{ $name };                         
  253.             }
  254.         }
  255.  
  256.         return $href;
  257.     }
  258.     
  259.     ### should we skip this module?
  260.     sub ignore_me {
  261.         my $mod = shift;
  262.         
  263.         for my $pat ( @ignore ) {
  264.             return $pat if $mod->module =~ /$pat/i;
  265.             return $pat if $mod->package_name =~ /$pat/i;
  266.         }
  267.         return;
  268.     }   
  269. }     
  270.  
  271.  
  272. my %done;
  273. for my $name (@modules) {
  274.  
  275.     my $obj;
  276.     
  277.     ### is it a tarball? then we get it locally and transform it
  278.     ### and it's dependencies into .debs
  279.     if( $tarball ) {
  280.         ### make sure we use an absolute path, so chdirs() dont
  281.         ### mess things up
  282.         $name = File::Spec->rel2abs( $name ); 
  283.  
  284.         ### ENOTARBALL?
  285.         unless( -e $name ) {
  286.             warn loc("Archive '$name' does not exist");
  287.             next;
  288.         }
  289.         
  290.         $obj = CPANPLUS::Module::Fake->new(
  291.                         module  => basename($name),
  292.                         path    => dirname($name),
  293.                         package => basename($name),
  294.                     );
  295.  
  296.         ### if it's a traditional CPAN package, we can tidy
  297.         ### up the module name some
  298.         $obj->module( $obj->package_name ) if $obj->package_name;
  299.  
  300.         ### get the version from the package name
  301.         $obj->version( $obj->package_version || 0 );
  302.  
  303.         ### set the location of the tarball
  304.         $obj->status->fetch($name);
  305.  
  306.     ### plain old cpan module?    
  307.     } else {
  308.  
  309.         ### find the corresponding module object ###
  310.         $obj = $cb->parse_module( module => $name ) or (
  311.                 warn "Cannot make a module object out of ".
  312.                         "'$name' -- skipping\n",
  313.                 next );
  314.     }
  315.  
  316.     ### you banned it?
  317.     if( my $pat = ban_me( $obj ) ) {
  318.         warn loc("'%1' found in your ban list (%2) -- skipping\n",
  319.                     $obj->module, $pat );
  320.         next;
  321.     }        
  322.     
  323.     ### or just ignored it? 
  324.     if( my $pat = ignore_me( $obj ) ) {
  325.         warn loc("'%1' found in your ignore list (%2) -- skipping\n",
  326.                     $obj->module, $pat );
  327.         next;
  328.     }        
  329.     
  330.  
  331.     my $target  = $opts->{'install'} ? 'install' : 'create';
  332.     my $dist    = eval { 
  333.                     local $SIG{ALRM} = sub { die bless {}, ALARM_CLASS }
  334.                         if $timeout;
  335.                         
  336.                     alarm $timeout || 0;
  337.  
  338.                     my $dist_opts = $opts->{'dist-opts'} || {};
  339.  
  340.                     my $rv = $obj->install(   
  341.                             prereq_target   => $target,
  342.                             target          => $target,
  343.                             keep_source     => $keep,
  344.                             prereq_build    => $prereqbuild,
  345.  
  346.                             ### any passed arbitrary options
  347.                             %$dist_opts,
  348.                     );
  349.                     
  350.                     alarm 0; 
  351.  
  352.                     $rv;
  353.                 }; 
  354.                 
  355.     ### set here again, in case the install dies
  356.     alarm 0;
  357.  
  358.     ### install failed due to a 'die' in our prereq skipper?
  359.     if( $@ and ref $@ and $@->isa( PREREQ_SKIP_CLASS ) ) {
  360.         warn loc("Dist creation of '%1' skipped: '%2'", 
  361.                     $obj->module, $@->() );
  362.         next;
  363.  
  364.     } elsif ( $@ and ref $@ and $@->isa( ALARM_CLASS ) ) {
  365.         warn loc("\nDist creation of '%1' skipped, build time exceeded: ".
  366.                  "%2 seconds\n", $obj->module, $timeout );
  367.         next;                    
  368.  
  369.     ### died for some other reason? just report and skip
  370.     } elsif ( $@ ) {
  371.         warn loc("Dist creation of '%1' failed: '%2'",
  372.                     $obj->module, $@ );
  373.         next;
  374.     }        
  375.  
  376.     ### we didn't get a dist object back?
  377.     unless ($dist and $obj->status->dist) {
  378.         warn loc("Unable to create '%1' dist of '%2'", $format, $obj->module);
  379.         next
  380.     }
  381.  
  382.     print "Created '$format' distribution for ", $obj->module,
  383.                 " to:\n\t", $obj->status->dist->status->dist, "\n";
  384. }
  385.  
  386.  
  387. sub parse_file {
  388.     my $file    = shift or return;
  389.     my $qr      = shift() ? 1 : 0;
  390.  
  391.     my $fh = OPEN_FILE->( $file ) or return;
  392.  
  393.     my @rv;
  394.     while( <$fh> ) {
  395.         chomp;
  396.         next if /^#/;                   # skip comments
  397.         next unless /\S/;               # skip empty lines
  398.         s/^(\S+).*/$1/;                 # skip extra info
  399.         push @rv, $qr ? qr/$_/ : $_;    # add pattern to the list
  400.     }
  401.    
  402.     return @rv;
  403. }
  404.  
  405. =head1 NAME
  406.  
  407. cpan2dist - The CPANPLUS distribution creator
  408.  
  409. =head1 DESCRIPTION
  410.  
  411. This script will create distributions of C<CPAN> modules of the format
  412. you specify, including its prerequisites. These packages can then be
  413. installed using the corresponding package manager for the format.
  414.  
  415. Note, you can also do this interactively from the default shell,
  416. C<CPANPLUS::Shell::Default>. See the C<CPANPLUS::Dist> documentation,
  417. as well as the documentation of your format of choice for any format
  418. specific documentation.
  419.  
  420. =head1 USAGE
  421.  
  422. =cut
  423.  
  424. sub usage {
  425.     my $me      = basename($0);
  426.     my $formats = join "\n", map { "\t\t$_" } sort keys %formats;
  427.  
  428.     my $usage = << '=cut';
  429. =pod
  430.  
  431.  Usage: cpan2dist [--format FMT] [OPTS] Mod::Name [Mod::Name, ...]
  432.         cpan2dist [--format FMT] [OPTS] --modulelist /tmp/mods.list
  433.         cpan2dist [--format FMT] [OPTS] --archive /tmp/dist [/tmp/dist2] 
  434.  
  435.     Will create a distribution of type FMT of the modules
  436.     specified on the command line, and all their prerequisites.
  437.     
  438.     Can also create a distribution of type FMT from a local
  439.     archive and all it's prerequisites
  440.  
  441. =cut
  442.  
  443.     $usage .= qq[
  444.     Possible formats are:
  445. $formats
  446.  
  447.     You can install more formats from CPAN!
  448.     \n];
  449.     
  450.     $usage .= << '=cut';
  451. =pod
  452.     
  453. Options:
  454.  
  455.     ### take no argument:
  456.     --help          Show this help message
  457.     --install       Install this package (and any prerequisites you built)
  458.                     after building it. 
  459.     --skiptest      Skip tests. Can be negated using --noskiptest
  460.     --force         Force operation. Can be negated using --noforce
  461.     --verbose       Be verbose. Can be negated using --noverbose
  462.     --keepsource    Keep sources after building distribution. Can be
  463.                     negated by --nokeepsource. May not be supported 
  464.                     by all formats
  465.     --makefile      Prefer Makefile.PL over Build.PL. Can be negated
  466.                     using --nomakefile. Defaults to your config setting
  467.     --buildprereq   Build packages of any prerequisites, even if they are
  468.                     already uptodate on the local system. Can be negated
  469.                     using --nobuildprereq. Defaults to false.
  470.     --archive       Indicate that all modules listed are actually archives
  471.     --flushcache    Update CPANPLUS' cache before commencing any operation
  472.     --defaults      Instruct ExtUtils::MakeMaker and Module::Build to use
  473.                     default answers during 'perl Makefile.PL' or 'perl
  474.                     Build.PL' calls where possible
  475.     --edit-metafile Edit the distributions metafile(s) before the distribution
  476.                     is built. Requires a configured editor.
  477.  
  478.     ### take argument:
  479.     --format      Installer format to use (defaults to config setting)
  480.     --ban         Patterns of module names to skip during installation,
  481.                   case-insensitive (affects prerequisites too)
  482.                   May be given multiple times
  483.     --banlist     File containing patterns that could be given to --ban
  484.                   Are appended to the ban list built up by --ban
  485.                   May be given multiple times.
  486.     --ignore      Patterns of modules to exclude from prereq list. Useful
  487.                   for when a prereq listed by a CPAN module is resolved 
  488.                   in another way than from its corresponding CPAN package
  489.                   (Match is done on both module name, and package name of
  490.                   the package the module is in, case-insensitive)
  491.     --ignorelist  File containing patterns that may be given to --ignore.
  492.                   Are appended to the ban list built up by --ignore.
  493.                   May be given multiple times.
  494.     --modulelist  File containing a list of modules that should be built.
  495.                   Are appended to the list of command line modules.
  496.                   May be given multiple times.
  497.     --logfile     File to log all output to. By default, all output goes
  498.                   to the console.
  499.     --timeout     The allowed time for buliding a distribution before
  500.                   aborting. This is useful to terminate any build that 
  501.                   hang or happen to be interactive despite being told not 
  502.                   to be. Defaults to 300 seconds. To turn off, you can 
  503.                   set it to 0.
  504.     --set-config  Change any options as specified in your config for this
  505.                   invocation only. See CPANPLUS::Config for a list of 
  506.                   supported options.
  507.     --set-program Change any programs as specified in your config for this
  508.                   invocation only. See CPANPLUS::Config for a list of 
  509.                   supported programs.
  510.     --dist-opts   Arbitrary options passed along to the chosen installer
  511.                   format's prepare()/create() routine. Please see the
  512.                   documentation of the installer of your choice for 
  513.                   options it accepts.
  514.  
  515.     ### builtin lists
  516.     --default-banlist    Use our builtin banlist. Works just like --ban
  517.                          and --banlist, but with pre-set lists. See the
  518.                          "Builtin Lists" section for details.
  519.     --default-ignorelist Use our builtin ignorelist. Works just like 
  520.                          --ignore and --ignorelist but with pre-set lists. 
  521.                          See the "Builtin Lists" section for details.
  522.  
  523. Examples:
  524.  
  525.     ### build a debian package of DBI and it's prerequisites, 
  526.     ### don't bother running tests
  527.     cpan2dist --format CPANPLUS::Dist::Deb --buildprereq --skiptest DBI
  528.     
  529.     ### build a debian package of DBI and it's prerequisites and install them
  530.     cpan2dist --format CPANPLUS::Dist::Deb --buildprereq --install DBI
  531.     
  532.     ### Build a package, whose format is determined by your config, of 
  533.     ### the local tarball, reloading cpanplus' indices first and using
  534.     ### the tarballs Makefile.PL if it has one.
  535.     cpan2dist --makefile --flushcache --archive /path/to/Cwd-1.0.tgz
  536.     
  537.     ### build a package from Net::FTP, but dont build any packages or
  538.     ### dependencies whose name match 'Foo', 'Bar' or any of the 
  539.     ### patterns mentioned in /tmp/ban
  540.     cpan2dist --ban Foo --ban Bar --banlist /tmp/ban Net::FTP
  541.     
  542.     ### build a package from Net::FTP, but ignore it's listed dependency
  543.     ### on IO::Socket, as it's shipped per default with the OS we're on
  544.     cpan2dist --ignore IO::Socket Net::FTP
  545.     
  546.     ### building all modules listed, plus their prerequisites
  547.     cpan2dist --ignorelist /tmp/modules.ignore --banlist /tmp/modules.ban 
  548.       --modulelist /tmp/modules.list --buildprereq --flushcache 
  549.       --makefile --defaults
  550.     
  551.     ### pass arbitrary options to the format's prepare()/create() routine
  552.     cpan2dist --dist-opts deb_version=3 --dist-opts prefix=corp
  553.  
  554. =cut
  555.     
  556.     $usage .= qq[
  557. Builtin Lists:
  558.  
  559.     Ignore list:] . _default_ignore_list() . qq[
  560.     Ban list:] .    _default_ban_list();
  561.     
  562.     ### strip the pod directives
  563.     $usage =~ s/=pod\n//g;
  564.     
  565.     return $usage;
  566. }
  567.  
  568. =pod
  569.  
  570. =head1 Built-In Filter Lists
  571.  
  572. Some modules you'd rather not package. Some because they
  573. are part of core-perl and you dont want a new package.
  574. Some because they won't build on your system. Some because
  575. your package manager of choice already packages them for you.
  576.  
  577. There may be a myriad of reasons. You can use the C<--ignore>
  578. and C<--ban> options for this, but we provide some built-in
  579. lists that catch common cases. You can use these built-in lists
  580. if you like, or supply your own if need be.
  581.  
  582. =head2 Built-In Ignore List
  583.  
  584. =pod 
  585.  
  586. You can use this list of regexes to ignore modules matching
  587. to be listed as prerequisites of a package. Particulaly useful
  588. if they are bundled with core-perl anyway and they have known
  589. issues building.
  590.  
  591. Toggle it by supplying the C<--default-ignorelist> option.
  592.  
  593. =cut
  594.  
  595. sub _default_ignore_list {
  596.  
  597.     my $list = << '=cut';
  598. =pod
  599.  
  600.     ^IO$                    # Provided with core anyway
  601.     ^Cwd$                   # Provided with core anyway
  602.     ^File::Spec             # Provided with core anyway
  603.     ^Config$                # Perl's own config, not shipped separately
  604.     ^ExtUtils::MakeMaker$   # Shipped with perl, recent versions 
  605.                             # have bug 14721 (see rt.cpan.org)
  606.     ^ExtUtils::Install$     # Part of of EU::MM, same reason    
  607.  
  608. =cut
  609.  
  610.     return $list;
  611. }
  612.  
  613. =head2 Built-In Ban list
  614.  
  615. You can use this list of regexes to disable building of these
  616. modules altogether.
  617.  
  618. Toggle it by supplying the C<--default-banlist> option.
  619.  
  620. =cut
  621.  
  622. sub _default_ban_list {
  623.  
  624.     my $list = << '=cut';
  625. =pod
  626.  
  627.     ^GD$                # Needs c libaries
  628.     ^Berk.*DB           # DB packages require specific options & linking
  629.     ^DBD::              # DBD drives require database files/headers
  630.     ^XML::              # XML modules usually require expat libraries
  631.     Apache              # These usually require apache libraries
  632.     SSL                 # These usually require SSL certificates & libs
  633.     Image::Magick       # Needs ImageMagick C libraries
  634.     Mail::ClamAV        # Needs ClamAV C Libraries
  635.     ^Verilog            # Needs Verilog C Libraries
  636.     ^Authen::PAM$       # Needs PAM C libraries & Headers
  637.  
  638. =cut
  639.  
  640.     return $list;
  641. }
  642.  
  643. __END__
  644.  
  645. =head1 SEE ALSO
  646.  
  647. L<CPANPLUS::Dist>, L<CPANPLUS::Module>, L<CPANPLUS::Shell::Default>,
  648. C<cpanp>
  649.  
  650. =head1 BUG REPORTS
  651.  
  652. Please report bugs or other issues to E<lt>bug-cpanplus@rt.cpan.org<gt>.
  653.  
  654. =head1 AUTHOR
  655.  
  656. This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
  657.  
  658. =head1 COPYRIGHT
  659.  
  660. The CPAN++ interface (of which this module is a part of) is copyright (c) 
  661. 2001 - 2007, Jos Boumans E<lt>kane@cpan.orgE<gt>. All rights reserved.
  662.  
  663. This library is free software; you may redistribute and/or modify it 
  664. under the same terms as Perl itself.
  665.  
  666. =cut
  667.  
  668. # Local variables:
  669. # c-indentation-style: bsd
  670. # c-basic-offset: 4
  671. # indent-tabs-mode: nil
  672. # End:
  673. # vim: expandtab shiftwidth=4:
  674.